import {
  observe
} from "../observe/index";
import {
  newArrayProtoMethods,
  observeArray,
  dependArray
} from './array';
import Dep from "./dep";

// Observer这个类帮忙观察数据
class Observer {
  constructor(data) {
    // console.log(data);
    //{message: 'hello vue2.x', nums: Array(3), person: {…}}

    //定义一个dep实例
    this.dep = new Dep();

    // console.log(this.dep);
    //Dep {id: 4, subs: Array(0)}

    //让所有对象都多定义一个属性
    Object.defineProperty(data, '__ob__', {
      //当访问xxx.__ob__.方法 的时候触发get()返回Observer实例
      get: () => this
    });

    //处理data数据底下的数组和数组底下的方法进行数据响应式
    if (Array.isArray(data)) {
      //重新push方法,给原来push方法的同时再增加自己的逻辑
      //将新创建的数组方法赋值给data数据底下的数组原型
      data.__proto__ = newArrayProtoMethods;

      //让dep实例进入observeArray方法里

      observeArray(data);
      // console.log(data);

    } else {
      //处理对象
      //重新定义数据
      this.walk(data);
    }


  }

  walk(data) {
    //循环对象拿到每一个属性进行重新定义
    // 注意：用Object.keys()而不是for in 避免去原型链上找
    let keys = Object.keys(data);
    // console.log(keys);
    //['message', 'nums', 'person']

    for (let i = 0; i < keys.length; i++) {
      let key = keys[i];
      let value = data[key];

      defineReactive(data, key, value);
    }
  }
}

export function defineReactive(data, key, value) {
  //对于对象嵌套的属性进行再次的监听并做响应式处理
  let childOb = observe(value);

  //data数据里属性的value有可能对象/字符串/数组


  // debugger;

  //注意：data里每个属性对应一个实例化的dep
  /**
   * 假如data(){
   *   return {
   *     message: 'hello vue2.x',
   *     nums: [1, 4, 6],
   *     person: {
   *       name: 'mike',
   *       age: 20
   *     }
   *   }
   * }
   * 
   * 这里有5个属性，对应会实例化5个dep,每个dep对应一个唯一的id
   */
  //实例化Dep
  let dep = new Dep();
  // console.log(dep);
  //Dep {id: 0, subs: Array(0)}
  //Dep {id: 1, subs: Array(0)}
  //...

  //对每一个属性进行重新定义
  //Object.defineProperty()不兼容ie8及以下
  Object.defineProperty(data, key, {

    //用户读取data对象属性时
    get() {
      // debugger;
      // console.log('getter执行了！');

      //Dep.target => Watcher 
      if (Dep.target) {
        // console.log(Dep.target);
        //Watcher {vm: Vue, opts: {…}, id: 0, expOrFn: ƒ, cb: ƒ, …}
        //....

        //证明watcher已经在Dep.target里面了
        //执行订阅动作
        dep.depend(Dep.target);

        // console.log(dep.subs);
        //[Watcher]

        //如果有嵌套的属性值 有可能是数组/对象
        if (childOb) {
          // console.log(childOb);
          //Observer {dep: Dep}

          //利用Observer实例下dep底下的依赖收集方法进行数组的依赖收集
          //把数组相关的watcher放入dep里面，一旦数组发生变化，通知watcher进行重新的渲染
          childOb.dep.depend();

          //针对数组嵌套时收集数组依赖
          dependArray(value);
        }

      }

      return value;
    },
    set(newValue) {
      //执行发布动作
      dep.notify();


      if (newValue === value) return;
      observe(newValue);
      value = newValue;
    }
  });
}

export default Observer;